﻿using FlyingEye.HttpServices;
using FlyingEye.Monitors.Models;
using FlyingEye.Monitors.Repositories;
using Microsoft.Extensions.Logging;
using Volo.Abp.Application.Services;

namespace FlyingEye.DeviceMonitor
{
    /// <summary>
    /// 设备监视服务
    /// </summary>
    public class DeviceMonitorService : ApplicationService
    {
        private readonly IDeviceClientRepository _deviceClientRepository;
        private readonly IReadNumericValueTaskRepository _readNumericValueTaskRepository;
        private readonly IReadStringValueTaskRepository _readStringValueTaskRepository;

        private readonly DeviceHubService _deviceClientService;
        private readonly MessageDispatcherService _dispatcher;
        private static readonly ObjectStatusManager _taskStatusManager = new ObjectStatusManager();
        private readonly ILogger<DeviceMonitorService> _logger;
        private readonly TaskNotificationService _taskNotificationService;

        public DeviceMonitorService(
            DeviceHubService deviceClientService,
            MessageDispatcherService dispatcher,
            ILogger<DeviceMonitorService> logger,
            TaskNotificationService taskNotificationService,
            IDeviceClientRepository deviceClientRepository,
            IReadNumericValueTaskRepository readNumericValueTaskRepository,
            IReadStringValueTaskRepository readStringValueTaskRepository)
        {
            _deviceClientService = deviceClientService;
            _dispatcher = dispatcher;
            _logger = logger;
            _taskNotificationService = taskNotificationService;
            _deviceClientRepository = deviceClientRepository;
            _readNumericValueTaskRepository = readNumericValueTaskRepository;
            _readStringValueTaskRepository = readStringValueTaskRepository;
        }

        /// <summary>
        /// 执行所有的轮询任务
        /// </summary>
        public async Task ExecuteMonitorTasksAsync()
        {
            int pageIndex = 0; // 假设从 0 开始
            const int pageSize = 100;

            // 重置任务状态令牌
            _taskStatusManager.UpdateObjectStatusToken();

            while (true)
            {
                var clients = await _deviceClientRepository.GetPagedListAsync(
                    skipCount: pageIndex * pageSize, // 确认 pageIndex 起始值
                    maxResultCount: pageSize,
                    sorting: "Id" // 确保 Id 有索引
                );

                if (!clients.Any())
                {
                    break;
                }

                foreach (var deviceClient in clients)
                {
                    await ExecuteReadNumericValueTasksAsync(deviceClient);
                    await ExecuteReadStringValueTasksAsync(deviceClient);
                }

                // 更新分页索引
                pageIndex++;
            }

            // 清理垃圾任务状态
            _taskStatusManager.ClearTrashObjectStatus();
        }

        /// <summary>
        /// 执行读取字符串值任务
        /// </summary>
        private async Task ExecuteReadStringValueTasksAsync(DeviceClientModel deviceClient)
        {
            int pageIndex = 0; // 假设从 0 开始
            const int pageSize = 100;
            var parallelOptions = new ParallelOptions();
            parallelOptions.MaxDegreeOfParallelism = 25; // 并行度
            var query = await _readStringValueTaskRepository.GetQueryableAsync();

            while (true)
            {
                var tasks = query.OrderBy(e => e.Id)
                     .Where(t => t.DeviceClientId == deviceClient.Id && t.Enable == true)
                     .Skip(pageIndex * pageSize)
                     .Take(pageSize);

                if (!tasks.Any())
                {
                    break;
                }

                await Parallel.ForEachAsync(tasks, parallelOptions, async (task, token) =>
                {
                    try
                    {
                        // 任务时间未到，直接返回
                        if (_taskStatusManager.CanAccessObject(task.Id, task.Interval))
                        {
                            await ReadStringValueAsync(deviceClient, task);
                        }
                    }
                    catch (Exception ex)
                    {
                        _logger.LogError(ex, $"执行任务 {task.Id} 失败！");
                    }
                });

                // 更新分页索引
                pageIndex++;
            }
        }

        /// <summary>
        /// 执行读取数值任务
        /// </summary>
        public async Task ExecuteReadNumericValueTasksAsync(DeviceClientModel deviceClient)
        {
            int pageIndex = 0; // 假设从 0 开始
            const int pageSize = 100;
            var parallelOptions = new ParallelOptions();
            parallelOptions.MaxDegreeOfParallelism = 25; // 并行度
            var query = await _readNumericValueTaskRepository.GetQueryableAsync();

            while (true)
            {
                var tasks = query.OrderBy(e => e.Id)
                     .Where(t => t.DeviceClientId == deviceClient.Id && t.Enable == true)
                     .Skip(pageIndex * pageSize)
                     .Take(pageSize);

                if (!tasks.Any())
                {
                    break;
                }

                foreach (var task in tasks)
                {
                    try
                    {
                        // 任务时间未到，直接返回
                        if (_taskStatusManager.CanAccessObject(task.Id, task.Interval))
                        {
                            await ReadNumericValueAsync(deviceClient, task);
                        }
                    }
                    catch (Exception ex)
                    {
                        _logger.LogError(ex, $"执行任务 {task.Id} 失败！");
                    }
                }

                // 更新分页索引
                pageIndex++;
            }
        }

        /// <summary>
        /// 执行读取字符串值任务
        /// </summary>
        private async Task ReadStringValueAsync(DeviceClientModel deviceClient, ReadStringValueTaskModel task)
        {
            var result = await _deviceClientService.ReadStringValueAsync(deviceClient.ResourceId,
                new HttpServices.Requests.ReadStringValueRequest(task.DeviceName, task.Address, task.Length));

            if (result.IsSuccess)
            {
                // 将结果派遣到客户端
                await _dispatcher.DispatcherAsync(
                    new DispatcherMessage(new ReadValueTask(
                         task.Id,
                        deviceClient.Id,
                        task.DeviceName,
                        task.TaskName,
                        task.Address,
                        "string",
                        task.Interval,
                        "read-string-value"), result.Value!));
            }
            else
            {
                // 将失败的任务通知到管理员群组
                var message = $"DeviceName: {task.DeviceName} Address: {task.Address} DataType: string Message:{result.Message}";
                await _taskNotificationService.NotifyFailureTaskToAdminGroupAsync(task.Id, task.TaskName, message);
            }
        }

        /// <summary>
        /// 执行读取数据值任务
        /// </summary>
        private async Task ReadNumericValueAsync(DeviceClientModel deviceClient, ReadNumericValueTaskModel task)
        {
            var result = await _deviceClientService.ReadNumericValeuAsync(deviceClient.ResourceId,
                new HttpServices.Requests.ReadNumericValueRequest(task.DeviceName, task.Address, task.DataType));

            if (result.IsSuccess)
            {
                // 将结果派遣到客户端
                await _dispatcher.DispatcherAsync(
                    new DispatcherMessage(new ReadValueTask(
                        task.Id,
                        deviceClient.Id,
                        task.DeviceName,
                        task.TaskName,
                        task.Address,
                        task.DataType,
                        task.Interval,
                        "read-numeric-value"), result.Value!));
            }
            else
            {
                // 将失败的任务通知到管理员群组
                var message = $"DeviceName: {task.DeviceName} Address: {task.Address} DataType: {task.DataType} Message:{result.Message}";
                await _taskNotificationService.NotifyFailureTaskToAdminGroupAsync(task.Id, task.TaskName, message);
            }
        }
    }
}
